import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

import jxl.write.WriteException;


public class Generation {
	
	public ArrayList<Chromosome> chromosomeList;
	
	public Generation(int size){
		
		chromosomeList = new ArrayList<Chromosome>();
		for(int i = 0 ; i < size ; i++)
			chromosomeList.add(new Chromosome(true));
		
	}
	
	//return the index of the random selected chromosome
	private int getWeightRandomChormo1(){
		//Random r = new Random (Data.crossOverRandomGenerator.nextInt());
		int inteval = Data.generationSize * (Data.generationSize+1) / 2;
		int result=1,bottom=-1,top=1,number=Data.crossOverRandomGenerator.nextInt(inteval);
		boolean finish=false;
		while(!finish){
			if(number > bottom && number <= top )
				finish=true;
			else{
				bottom=top-1;
				top+=result;
				result++;
			}
		}
		if(result==(Data.generationSize+1))
			return 0;
		return this.chromosomeList.size()-result;
	}
	
	private int getWeightRandomChormo(){
		int sum = 0 ;
		int maxTimeInPrison = this.chromosomeList.get(0).gamesPlayed * Data.gameLength * 5;
		int chromosScores[] = new  int[chromosomeList.size()];
		for(int i = 0; i < chromosomeList.size(); i++){
			chromosScores[i] = (int) (maxTimeInPrison - chromosomeList.get(i).timeInPrison);
			sum+=chromosScores[i];
		}
		long random = Data.crossOverRandomGenerator.nextInt(sum);
		int i=0;
		int bottom=0;
		int top = chromosScores [0];
		while(true){
			if(random >= bottom && random <=top )
				return i ;
			else{
				bottom=top;
				i++;
				top=top+chromosScores[i];
			}
		}
	}
	
	public static Generation createNewGeneration(Generation oldGeneration){

		Generation newGeneration= new Generation(0); // empty generation
		
		// play games for each chromosome in oldGeneration
		switch (Data.fitnessMethod){
		
			case AgaintsRandomFromGeneration:
				
				for(int i=0 ; i < oldGeneration.chromosomeList.size() ; i++){
					Chromosome current = oldGeneration.chromosomeList.get(i);
					int count=Data.fromGenerationGamesToPlayPerChromo;
					while (count > 0) {
						int rand=Data.matcherRandomGenerator.nextInt(oldGeneration.chromosomeList.size());
						if(rand!=i){ //case we didn't choose the same chromosome
							current.play(oldGeneration.chromosomeList.get(rand));
							count--;
						}
					}
				}
			break;
			
			case AgainstAllGeneration :
				
				for(int i=0 ; i < oldGeneration.chromosomeList.size() ; i++){
					Chromosome current = oldGeneration.chromosomeList.get(i);
					//play against all generation
					for(int j=0 ; j < oldGeneration.chromosomeList.size() ; j++){
						if(j!=i) //case we didn't choose the same chromosome
							current.play(oldGeneration.chromosomeList.get(j));
					}
				}
				
			break;
			
			case AgainstNewRandoms :
				for(int i=0 ; i < oldGeneration.chromosomeList.size() ; i++){
					Chromosome current = oldGeneration.chromosomeList.get(i);

					//play against new random chromosomes.
					for(int j=0 ; j <Data.newRandomGamesToPlayPerChromo ; j++){
						//create a new random chromosome
						Random r = new Random(Data.ChromoCreationRandomGenerator.nextInt());
						int tempArr[]=new int[Data.chromozomSize];
						for(int k=0;k<Data.chromozomSize;k++){
							tempArr[k]=r.nextInt(2);
						}
						Chromosome temp = new Chromosome(tempArr);
						//play against it
						current.play(temp);
					}
				}
			
			break;
			
			case AgainstAllGenerationAndNewRandoms :
			
				for(int i=0 ; i < oldGeneration.chromosomeList.size() ; i++){
					Chromosome current = oldGeneration.chromosomeList.get(i);
					//play against all generation
					for(int j=0 ; j < oldGeneration.chromosomeList.size() ; j++){
						if(j!=i) //case we didn't choose the same chromosome
							current.play(oldGeneration.chromosomeList.get(j));
					}
					//play against new random chromosomes.
					for(int j=0 ; j <Data.newRandomGamesToPlayPerChromo ; j++){
						//create a new random chromosome
						Random r = new Random(Data.ChromoCreationRandomGenerator.nextInt());
						int tempArr[]=new int[Data.chromozomSize];
						for(int k=0;k<Data.chromozomSize;k++){
							tempArr[k]=r.nextInt(2);
						}
						Chromosome temp = new Chromosome(tempArr);
						//play against it
						current.play(temp);
					}
				}
				
			break;
			
			case Against10PercentOfChromosoms : 
				
				int numGames = oldGeneration.chromosomeList.size()/10;
				System.out.println("numGames " +numGames);
				
				for(int i=0; i<oldGeneration.chromosomeList.size(); i++){
					
					Chromosome current = oldGeneration.chromosomeList.get(i);
					//play against  10% of the chromosoms
					for(int j=0; j<numGames;){
						
						int index = Data.matcherRandomGenerator.nextInt(oldGeneration.chromosomeList.size());
						
						//in case that are not the same chromosoms
						if(index!=i){							
							current.play(oldGeneration.chromosomeList.get(index));
							j++;
						}
						
					}
					
				}
				//end of the prime for
			
			break;
		
		}
		

		// sort the chromosomes in  descending order
		Collections.sort(oldGeneration.chromosomeList,new MinTimeInPrisonComparator());
		Collections.reverse(oldGeneration.chromosomeList);
		
		//System.out.print(oldGeneration.chromosomeList.get(0).gamesWon+" - "+oldGeneration.chromosomeList.get(0).timeInPrison+";\t");
		
		//data for statistics
		try {
			WriteExcel1.instance.write(oldGeneration);
		} catch (WriteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		//positive elitism
		int elitismNumber = (int) (Data.positiveElitismSize * Data.generationSize);
		for(int i=0 ; i < elitismNumber ; i++)
			newGeneration.chromosomeList.add(new Chromosome(oldGeneration.chromosomeList.get(i)));
		
		//cross over 
		int crossOverMetodNumber = (int) (Data.crossOverMethodSize * Data.generationSize);
		while(crossOverMetodNumber > 0){
			//get index of parents
			int index1=oldGeneration.getWeightRandomChormo();
			int index2=oldGeneration.getWeightRandomChormo();
			//case different parents
			if(index1!=index2){
				Chromosome father = oldGeneration.chromosomeList.get(index1);
				Chromosome mother = oldGeneration.chromosomeList.get(index2);
				//get new sons
				Chromosome[] newSons = Chromosome.crossOver(father, mother);
				//add the sons to next generation
				newGeneration.chromosomeList.add(newSons[0]);
				newGeneration.chromosomeList.add(newSons[1]);
				//advance
				crossOverMetodNumber=crossOverMetodNumber-2;
			}
		}
		
		// only mutate
		int remain=Data.generationSize - newGeneration.chromosomeList.size();
		for(int i=0 ; i < remain ; i++){
			int inx = oldGeneration.getWeightRandomChormo();
			Chromosome temp = oldGeneration.chromosomeList.get(inx);
			temp= new  Chromosome(temp);
			temp.mutate();
			newGeneration.chromosomeList.add(temp);
		}
		
		return newGeneration;
		
	}

	
}
